/*
 * MIT License
 *
 * Copyright (c) 2020 wen.gu <454727014@qq.com>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

/***************************************************************************
 * Name: feild.h
 *
 * Purpose: implementation feild
 *
 * Developer:
 *   wen.gu , 2020-04-27
 *
 * TODO:
 *
 ***************************************************************************/

#ifndef __ARA_COM_SKELETON_FEILD_H__
#define __ARA_COM_SKELETON_FEILD_H__
/******************************************************************************
 **    INCLUDES
 ******************************************************************************/
#include <functional>
#include <type_traits>

#include "ara/com/types.h"
#include "ara/com/skeleton/service_skeleton.h"
#include "ara/core/future.h"
#include "ara/core/string.h"
#include "ara/com/runtime/message.h"
/******************************************************************************
 **    MACROS
 ******************************************************************************/


/******************************************************************************
 **    TYPE DEFINITIONS
 ******************************************************************************/
namespace ara
{
namespace com
{
namespace skeleton
{
namespace feild
{

/******************************************************************************
 **    CLASSES/FUNCTIONS DEFINITIONS
 ******************************************************************************/

template<bool hasGetter, typename FeildType>
class FeildGetterBase
{
    using FeildRetType = ara::core::Future<FeildType>;
    using FeildGetHandler = std::function<FeildRetType()>;
    using MessageType = ara::com::runtime::MessageType;
    using MessagePtr = ara::com::runtime::MessagePtr;
    ServiceSkeleton* mProvider;
    ara::core::String mName;
public:
    FeildGetterBase(ServiceSkeleton* ss, const ara::core::String& name)
        :mProvider(ss),
        mName(name)
    {

    }

    void RegisterGetHandler(FeildGetHandler getHandler)
    {
        this->mProvider->RegisterReceiveHandler(this->mName, MessageType::kFeildGet, [this, getHandler](MessagePtr msg)
        {
            FeildRetType ret = getHandler();
            ret.then([this, msg](FeildRetType&& f)->bool
            {
                try
                {
                    FeildType val = f.get();

                    ServiceSkeleton::SerializerPtr sp = this->mProvider->GetSerializer(msg->BindingType());
                    sp->Write(val);
                    uint32_t plsize = 0;
                    msg->SetPayload(sp->GetPayload(plsize));
                    msg->SetPayloadSize(plsize);
                    msg->SetType(ara::com::runtime::MessageType::kFeildGetAck);
                    return this->mProvider->SendFeildAck(msg);
                }
                catch (ComException& e)
                { /** todo refine me */
                    //return mProvider->SendError(msg, e.Error().Value());
                }
                catch (std::exception& e)
                {/** todo, refineme?? */
                    //return mProvider->SendError(msg, ComErrc::kUnknownError);
                }
            });
        });
    }
};

template<typename FeildType>
class FeildGetterBase<false, FeildType>
{
public:
    FeildGetterBase(ServiceSkeleton* ss, const ara::core::String& name) {}
    /** false case, not include geter register function */
};

template<bool hasSetter, typename FeildType>
class FeildSetterBase
{
    using FeildRetType = ara::core::Future<FeildType>;
    using FeildSetHandler = std::function<FeildRetType(const FeildType& value)>;
    using MessageType = ara::com::runtime::MessageType;
    using MessagePtr = ara::com::runtime::MessagePtr;
    ServiceSkeleton* mProvider;
    ara::core::String mName;
public:
    FeildSetterBase(ServiceSkeleton* ss, const ara::core::String& name)
        :mProvider(ss),
        mName(name)
    {
        /** todo something */
    }

    void RegisterSetHandler(FeildSetHandler setHandler)
    {
        this->mProvider->RegisterReceiveHandler(this->mName, MessageType::kFeildSet, [this, setHandler](MessagePtr msg)
        {
            ServiceSkeleton::DeserializerPtr dp = this->mProvider->GetDeserializer(msg->payload.get(), msg->payloadSize);
            FeildType val;
            dp->Read(val);
            FeildRetType ret = setHandler(val);

            ret.then([this, msg](FeildRetType&& f)->bool
            {
                try
                {
                    FeildType val = f.get();

                    ServiceSkeleton::SerializerPtr sp = this->mProvider->GetSerializer();
                    sp->Write(val);
                    msg->payload = sp->GetPayload(msg->payloadSize);
                    msg->type = ara::com::runtime::MessageType::kFeildSetAck;
                    return this->mProvider->SendFeildAck(msg);
                }
                catch (ComException& e)
                { /** todo refine me */
                    //return mProvider->SendError(msg, e.Error().Value());
                }
                catch (std::exception& e)
                {/** todo, refineme?? */
                    //return mProvider->SendError(msg, ComErrc::kUnknownError);
                }
            });

        });
    }
};

template<typename FeildType>
class FeildSetterBase<false, FeildType>
{
public:
    FeildSetterBase(ServiceSkeleton* ss, const ara::core::String& name){}
    /** false case, not include setter register function provide */
};


template<bool hasSetter, typename FeildType>
class FeildNotiferBase
{
    using MessageType = ara::com::runtime::MessageType;
    using MessagePtr = ara::com::runtime::MessagePtr;
    ServiceSkeleton* mProvider;
    ara::core::String mName;
public:
    FeildNotiferBase(ServiceSkeleton* ss, const ara::core::String& name)
        :mProvider(ss),
        mName(name)
    {
        /** todo something */
    }
    void Update(const FeildType& value)
    {
        ServiceSkeleton::SerializerPtr sp = mProvider->GetSerializer();
        sp->Write(value);
        MessagePtr msg = mProvider->MakeMessage(MessageType::kFeildNotify);
        msg->name = mName;
        msg->payload = sp->GetPayload(msg->payloadSize);
 
        mProvider->SendFeildAck(msg);
    }
};

template<typename FeildType>
class FeildNotiferBase<false, FeildType>
{
public:
    FeildNotiferBase(ServiceSkeleton* ss, const ara::core::String& name) {};
    /** false case, not include notify function provide */
};


template<typename FeildType, bool hasGetter, bool hasSetter, bool hasNotifer>
class Feild: public FeildGetterBase<hasGetter, FeildType>,
             public FeildSetterBase<hasSetter, FeildType>,
             public FeildNotiferBase<hasNotifer, FeildType>
{
public:
    Feild(ServiceSkeleton* ss, const ara::core::String& name)
        :FeildNotiferBase<hasNotifer, FeildType>(ss, name),
        FeildSetterBase<hasSetter, FeildType>(ss, name),
        FeildGetterBase<hasGetter, FeildType>(ss, name)
    {
        /** todo something */
    }

    ~Feild()
    {
        /** todo something */
    }
public:
    bool HasSetter() const { return hasSetter; }
    bool HasGetter() const { return hasGetter; }
    bool HasNotifer() const { return hasNotifer; }
};


} /** namespace feild */
} /** namespace skeleton */
} /** namespace com */
} /** namespace ara */

#endif /** !__ARA_COM_SKELETON_FEILD_H__ */

